home *** CD-ROM | disk | FTP | other *** search
/ Risc World 5 / Risc World 5.iso / SOFTWARE / Issue3 / Games / xrick / !xrick / src / c / syssnd < prev    next >
Text File  |  2004-06-24  |  9KB  |  434 lines

  1. /*
  2.  * xrick/src/syssnd.c
  3.  *
  4.  * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net). All rights reserved.
  5.  *
  6.  * The use and distribution terms for this software are contained in the file
  7.  * named README, which can be found in the root of this distribution. By
  8.  * using this software in any fashion, you are agreeing to be bound by the
  9.  * terms of this license.
  10.  *
  11.  * You must not remove this notice, or any other, from this software.
  12.  */
  13.  
  14. #include <SDL.h>
  15. #include <stdlib.h>
  16. #include <memory.h>
  17.  
  18. #include "config.h"
  19.  
  20. #ifdef ENABLE_SOUND
  21.  
  22. #include "system.h"
  23. #include "game.h"
  24. #include "syssnd.h"
  25. #include "debug.h"
  26. #include "data.h"
  27.  
  28. #define ADJVOL(S) (((S)*sndVol)/SDL_MIX_MAXVOLUME)
  29.  
  30. static U8 isAudioActive = FALSE;
  31. static channel_t channel[SYSSND_MIXCHANNELS];
  32.  
  33. static U8 sndVol = SDL_MIX_MAXVOLUME;  /* internal volume */
  34. static U8 sndUVol = SYSSND_MAXVOL;  /* user-selected volume */
  35. static U8 sndMute = FALSE;  /* mute flag */
  36.  
  37. static SDL_mutex *sndlock;
  38.  
  39. /*
  40.  * prototypes
  41.  */
  42. static int sdlRWops_open(SDL_RWops *context, char *name);
  43. static int sdlRWops_seek(SDL_RWops *context, int offset, int whence);
  44. static int sdlRWops_read(SDL_RWops *context, void *ptr, int size, int maxnum);
  45. static int sdlRWops_write(SDL_RWops *context, const void *ptr, int size, int num);
  46. static int sdlRWops_close(SDL_RWops *context);
  47. static void end_channel(U8);
  48.  
  49. /*
  50.  * Callback -- this is also where all sound mixing is done
  51.  *
  52.  * Note: it may not be that much a good idea to do all the mixing here ; it
  53.  * may be more efficient to mix samples every frame, or maybe everytime a
  54.  * new sound is sent to be played. I don't know.
  55.  */
  56. void syssnd_callback(UNUSED(void *userdata), U8 *stream, int len)
  57. {
  58.   U8 c;
  59.   S16 s;
  60.   U32 i;
  61.  
  62.   SDL_mutexP(sndlock);
  63.  
  64.   for (i = 0; i < (U32)len; i++) {
  65.     s = 0;
  66.     for (c = 0; c < SYSSND_MIXCHANNELS; c++) {
  67.       if (channel[c].loop != 0) {  /* channel is active */
  68.     if (channel[c].len > 0) {  /* not ending */
  69.       s += ADJVOL(*channel[c].buf - 0x80);
  70.       channel[c].buf++;
  71.       channel[c].len--;
  72.     }
  73.     else {  /* ending */
  74.       if (channel[c].loop > 0) channel[c].loop--;
  75.       if (channel[c].loop) {  /* just loop */
  76.         IFDEBUG_AUDIO2(sys_printf("xrick/audio: channel %d - loop\n", c););
  77.         channel[c].buf = channel[c].snd->buf;
  78.         channel[c].len = channel[c].snd->len;
  79.         s += ADJVOL(*channel[c].buf - 0x80);
  80.         channel[c].buf++;
  81.         channel[c].len--;
  82.       }
  83.       else {  /* end for real */
  84.         IFDEBUG_AUDIO2(sys_printf("xrick/audio: channel %d - end\n", c););
  85.         end_channel(c);
  86.       }
  87.     }
  88.       }
  89.     }
  90.     if (sndMute)
  91.       stream[i] = 0x80;
  92.     else {
  93.       s += 0x80;
  94.       if (s > 0xff) s = 0xff;
  95.       if (s < 0x00) s = 0x00;
  96.       stream[i] = (U8)s;
  97.     }
  98.   }
  99.  
  100.   memcpy(stream, stream, len);
  101.  
  102.   SDL_mutexV(sndlock);
  103. }
  104.  
  105. static void
  106. end_channel(U8 c)
  107. {
  108.     channel[c].loop = 0;
  109.     if (channel[c].snd->dispose)
  110.         syssnd_free(channel[c].snd);
  111.     channel[c].snd = NULL;
  112. }
  113.  
  114. void
  115. syssnd_init(void)
  116. {
  117.   SDL_AudioSpec desired, obtained;
  118.   U16 c;
  119.  
  120.   if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
  121.     IFDEBUG_AUDIO(
  122.       sys_printf("xrick/audio: can not initialize audio subsystem\n");
  123.       );
  124.     return;
  125.   }
  126.  
  127.   desired.freq = SYSSND_FREQ;
  128.   desired.format = AUDIO_U8;
  129.   desired.channels = SYSSND_CHANNELS;
  130.   desired.samples = SYSSND_MIXSAMPLES;
  131.   desired.callback = syssnd_callback;
  132.   desired.userdata = NULL;
  133.  
  134.   if (SDL_OpenAudio(&desired, &obtained) < 0) {
  135.     IFDEBUG_AUDIO(
  136.       sys_printf("xrick/audio: can not open audio (%s)\n", SDL_GetError());
  137.       );
  138.     return;
  139.   }
  140.  
  141.   sndlock = SDL_CreateMutex();
  142.   if (sndlock == NULL) {
  143.     IFDEBUG_AUDIO(sys_printf("xrick/audio: can not create lock\n"););
  144.     SDL_CloseAudio();
  145.     return;
  146.   }
  147.  
  148.   if (sysarg_args_vol != 0) {
  149.     sndUVol = sysarg_args_vol;
  150.     sndVol = SDL_MIX_MAXVOLUME * sndUVol / SYSSND_MAXVOL;
  151.   }
  152.  
  153.   for (c = 0; c < SYSSND_MIXCHANNELS; c++)
  154.     channel[c].loop = 0;  /* deactivate */
  155.  
  156.     isAudioActive = TRUE;
  157.     SDL_PauseAudio(0);
  158. }
  159.  
  160. /*
  161.  * Shutdown
  162.  */
  163. void
  164. syssnd_shutdown(void)
  165. {
  166.   if (!isAudioActive) return;
  167.  
  168.   SDL_CloseAudio();
  169.   SDL_DestroyMutex(sndlock);
  170.   isAudioActive = FALSE;
  171. }
  172.  
  173. /*
  174.  * Toggle mute
  175.  *
  176.  * When muted, sounds are still managed but not sent to the dsp, hence
  177.  * it is possible to un-mute at any time.
  178.  */
  179. void
  180. syssnd_toggleMute(void)
  181. {
  182.   SDL_mutexP(sndlock);
  183.   sndMute = !sndMute;
  184.   SDL_mutexV(sndlock);
  185. }
  186.  
  187. void
  188. syssnd_vol(S8 d)
  189. {
  190.   if ((d < 0 && sndUVol > 0) ||
  191.       (d > 0 && sndUVol < SYSSND_MAXVOL)) {
  192.     sndUVol += d;
  193.     SDL_mutexP(sndlock);
  194.     sndVol = SDL_MIX_MAXVOLUME * sndUVol / SYSSND_MAXVOL;
  195.     SDL_mutexV(sndlock);
  196.   }
  197. }
  198.  
  199. /*
  200.  * Play a sound
  201.  *
  202.  * loop: number of times the sound should be played, -1 to loop forever
  203.  * returns: channel number, or -1 if none was available
  204.  *
  205.  * NOTE if sound is already playing, simply reset it (i.e. can not have
  206.  * twice the same sound playing -- tends to become noisy when too many
  207.  * bad guys die at the same time).
  208.  */
  209. S8
  210. syssnd_play(sound_t *sound, S8 loop)
  211. {
  212.   S8 c;
  213.  
  214.   if (!isAudioActive) return -1;
  215.   if (sound == NULL) return -1;
  216.  
  217.   c = 0;
  218.   SDL_mutexP(sndlock);
  219.   while ((channel[c].snd != sound || channel[c].loop == 0) &&
  220.      channel[c].loop != 0 &&
  221.      c < SYSSND_MIXCHANNELS)
  222.     c++;
  223.   if (c == SYSSND_MIXCHANNELS)
  224.     c = -1;
  225.  
  226.   IFDEBUG_AUDIO(
  227.     if (channel[c].snd == sound && channel[c].loop != 0)
  228.       sys_printf("xrick/sound: already playing %s on channel %d - resetting\n",
  229.          sound->name, c);
  230.     else if (c >= 0)
  231.       sys_printf("xrick/sound: playing %s on channel %d\n", sound->name, c);
  232.     );
  233.  
  234.   if (c >= 0) {
  235.     channel[c].loop = loop;
  236.     channel[c].snd = sound;
  237.     channel[c].buf = sound->buf;
  238.     channel[c].len = sound->len;
  239.   }
  240.   SDL_mutexV(sndlock);
  241.  
  242.   return c;
  243. }
  244.  
  245. /*
  246.  * Pause
  247.  *
  248.  * pause: TRUE or FALSE
  249.  * clear: TRUE to cleanup all sounds and make sure we start from scratch
  250.  */
  251. void
  252. syssnd_pause(U8 pause, U8 clear)
  253. {
  254.   U8 c;
  255.  
  256.   if (!isAudioActive) return;
  257.  
  258.   if (clear == TRUE) {
  259.     SDL_mutexP(sndlock);
  260.     for (c = 0; c < SYSSND_MIXCHANNELS; c++)
  261.       channel[c].loop = 0;
  262.     SDL_mutexV(sndlock);
  263.   }
  264.  
  265.   if (pause == TRUE)
  266.     SDL_PauseAudio(1);
  267.   else
  268.     SDL_PauseAudio(0);
  269. }
  270.  
  271. /*
  272.  * Stop a channel
  273.  */
  274. void
  275. syssnd_stopchan(S8 chan)
  276. {
  277.   if (chan < 0 || chan > SYSSND_MIXCHANNELS)
  278.     return;
  279.  
  280.   SDL_mutexP(sndlock);
  281.   if (channel[chan].snd) end_channel(chan);
  282.   SDL_mutexV(sndlock);
  283. }
  284.  
  285. /*
  286.  * Stop a sound
  287.  */
  288. void
  289. syssnd_stopsound(sound_t *sound)
  290. {
  291.     U8 i;
  292.  
  293.     if (!sound) return;
  294.  
  295.     SDL_mutexP(sndlock);
  296.     for (i = 0; i < SYSSND_MIXCHANNELS; i++)
  297.         if (channel[i].snd == sound) end_channel(i);
  298.     SDL_mutexV(sndlock);
  299. }
  300.  
  301. /*
  302.  * See if a sound is playing
  303.  */
  304. int
  305. syssnd_isplaying(sound_t *sound)
  306. {
  307.     U8 i, playing;
  308.  
  309.     playing = 0;
  310.     SDL_mutexP(sndlock);
  311.     for (i = 0; i < SYSSND_MIXCHANNELS; i++)
  312.         if (channel[i].snd == sound) playing = 1;
  313.     SDL_mutexV(sndlock);
  314.     return playing;
  315. }
  316.  
  317.  
  318. /*
  319.  * Stops all channels.
  320.  */
  321. void
  322. syssnd_stopall(void)
  323. {
  324.     U8 i;
  325.  
  326.     SDL_mutexP(sndlock);
  327.     for (i = 0; i < SYSSND_MIXCHANNELS; i++)
  328.         if (channel[i].snd) end_channel(i);
  329.     SDL_mutexV(sndlock);
  330. }
  331.  
  332. /*
  333.  * Load a sound.
  334.  */
  335. sound_t *
  336. syssnd_load(char *name)
  337. {
  338.     sound_t *s;
  339.     SDL_RWops *context;
  340.     SDL_AudioSpec audiospec;
  341.  
  342.     /* alloc context */
  343.     context = malloc(sizeof(SDL_RWops));
  344.     context->seek = sdlRWops_seek;
  345.     context->read = sdlRWops_read;
  346.     context->write = sdlRWops_write;
  347.     context->close = sdlRWops_close;
  348.  
  349.     /* open */
  350.     if (sdlRWops_open(context, name) == -1)
  351.         return NULL;
  352.  
  353.     /* alloc sound */
  354.     s = malloc(sizeof(sound_t));
  355. #ifdef DEBUG
  356.     s->name = malloc(strlen(name) + 1);
  357.     strncpy(s->name, name, strlen(name) + 1);
  358. #endif
  359.  
  360.     /* read */
  361.     /* second param == 1 -> close source once read */
  362.     if (!SDL_LoadWAV_RW(context, 1, &audiospec, &(s->buf), &(s->len)))
  363.     {
  364.         free(s);
  365.         return NULL;
  366.     }
  367.  
  368.     s->dispose = FALSE;
  369.  
  370.     return s;
  371. }
  372.  
  373. /*
  374.  *
  375.  */
  376. void
  377. syssnd_free(sound_t *s)
  378. {
  379.     if (!s) return;
  380.     if (s->buf) SDL_FreeWAV(s->buf);
  381.     s->buf = NULL;
  382.     s->len = 0;
  383. }
  384.  
  385. /*
  386.  *
  387.  */
  388. static int
  389. sdlRWops_open(SDL_RWops *context, char *name)
  390. {
  391.     data_file_t *f;
  392.  
  393.     f = data_file_open(name);
  394.     if (!f) return -1;
  395.     context->hidden.unknown.data1 = (void *)f;
  396.  
  397.     return 0;
  398. }
  399.  
  400. static int
  401. sdlRWops_seek(SDL_RWops *context, int offset, int whence)
  402. {
  403.     return data_file_seek((data_file_t *)(context->hidden.unknown.data1), offset, whence);
  404. }
  405.  
  406. static int
  407. sdlRWops_read(SDL_RWops *context, void *ptr, int size, int maxnum)
  408. {
  409.     return data_file_read((data_file_t *)(context->hidden.unknown.data1), ptr, size, maxnum);
  410. }
  411.  
  412. static int
  413. sdlRWops_write(SDL_RWops *context, const void *ptr, int size, int num)
  414. {
  415.     /* not implemented */
  416.     return -1;
  417. }
  418.  
  419. static int
  420. sdlRWops_close(SDL_RWops *context)
  421. {
  422.     if (context)
  423.     {
  424.         data_file_close((data_file_t *)(context->hidden.unknown.data1));
  425.         free(context);
  426.     }
  427.     return 0;
  428. }
  429.  
  430. #endif /* ENABLE_SOUND */
  431.  
  432. /* eof */
  433.  
  434.